﻿using Microsoft.AspNetCore.Builder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace My.Framework.AspNetCore
{
    /// <summary>
    /// 启动模块
    /// </summary>
    /// <typeparam name="TStartupModule"></typeparam>
    public abstract class StartupModule<TStartupModule> : IStartupModule where TStartupModule : IStartupModule, new()
    {
        /// <summary>
        /// 启动器执行循序，从小到大的顺序执行
        /// </summary>
        public virtual int Order { get; set; } = 328;

        /// <summary>
        /// 程序启动器
        /// </summary>
        public StartupModule()
        {
            var startupType = this.GetType();

            if (startupType.FullName != typeof(TStartupModule).FullName)
            {
                throw new Exception($"当前实例类型 {startupType.FullName} 与 泛型传入类型 {typeof(TStartupModule).FullName} 不一致!");
            }
        }

        /// <summary>
        /// 配置服务
        /// </summary>
        /// <param name="webApplicationBuilder"></param>
        public virtual void ConfigureServices(WebApplicationBuilder webApplicationBuilder)
        {

        }

        /// <summary>
        /// 配置
        /// </summary>
        /// <param name="webApplication"></param>
        public virtual void Configure(WebApplication webApplication)
        {
			
		}

        /// <summary>
        /// 程序启动 可以获取程序启动 urls
        /// </summary>
        /// <param name="webApplication"></param>
        public virtual void ApplicationStarted(WebApplication webApplication)
        {

        }

        /// <summary>
        /// 程序停止中
        /// </summary>
        /// <param name="webApplication"></param>
        public virtual void ApplicationStopping(WebApplication webApplication)
        {

        }

        /// <summary>
        /// 程序已经停止
        /// </summary>
        /// <param name="webApplication"></param>
        public virtual void ApplicationStopped(WebApplication webApplication)
        {

        }

        /// <summary>
        /// 获取所有导入的启动器实例
        /// </summary>
        /// <returns></returns>
        public List<IStartupModule> GetAllImportStartup()
        {
            var startupType = this.GetType();

            CheckLoopImportStartup(startupType);

            var importStartupAttributes = startupType.GetCustomAttributes<DependsOnAttribute>();

            var startups = new List<IStartupModule>();
            startups.Add(this);

            foreach (var item in importStartupAttributes)
            {
                FindImportStartup(startups, item);
            }

            foreach (var item in startups)
            {
                if (startups.Count(w => w.GetType().FullName == item.GetType().FullName) > 1)
                {
                    startups.RemoveAt(startups.IndexOf(item));
                }
            }

            return startups.OrderBy(w => w.Order).ToList();
        }

        #region 私有

        /// <summary>
        /// 查找导入启动器特性
        /// </summary>
        /// <param name="startups"></param>
        /// <param name="importStartupAttribute"></param>
        private void FindImportStartup(List<IStartupModule> startups, DependsOnAttribute importStartupAttribute)
        {
            foreach (var startup in importStartupAttribute.GetStartups())
            {
                if (startups.Any(w => w.GetType().FullName == startup.GetType().FullName)) continue;

                startups.Add(startup);
            }

            // 循环启动器类型
            foreach (var startupType in importStartupAttribute.GetStartupTypes())
            {
                CheckLoopImportStartup(startupType);

                // 循环类型上的启动器标记
                var importStartupAttributes = startupType.GetCustomAttributes<DependsOnAttribute>();

                foreach (var item in importStartupAttributes)
                {
                    FindImportStartup(startups, item);
                }
            }
        }

        /// <summary>
        /// 验证循环导入启动器类型
        /// </summary>
        /// <param name="startupType"></param>
        private void CheckLoopImportStartup(Type startupType)
        {
            // 循环类型上的启动器标记
            var importStartupAttributes = startupType.GetCustomAttributes<DependsOnAttribute>();

            foreach (var item in importStartupAttributes)
            {
                // 验证启动器类型 是否被循环导入了 ImportStartupAttribute 中
                if (item.GetStartupTypes().Any(w => w.FullName == startupType.FullName))
                {
                    throw new Exception($"错误的导入。类型 {startupType.FullName} 特性标记 [ImportStartup] 不允许导入自身 {startupType.FullName} 启动器类型！");
                }
            }
        }

        #endregion
    }
}
